
package com.bitsfromspace.ubertorrent.tracker;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.bitsfromspace.ubertorrent.bencoding.BDecoder;
import com.bitsfromspace.ubertorrent.bencoding.BValue;
import com.bitsfromspace.ubertorrent.bencoding.BValue.Type;
import com.bitsfromspace.ubertorrent.peer.PeerAddress;

/**
 * TorrentResponse contains all the info returned from a {@link Tracker} announce.
 * @author: chris.oudeman
 * @since: Jan 6, 2011
 */
public class TorrentResponse {

	private final int interval;
	private final String trackerId;
	private final int complete;
	private final int incomplete;
	private final List<PeerAddress> peers;

	protected TorrentResponse(
			int interval,
			String trackerId,
			int complete,
			int incomplete,
			List<PeerAddress> peers) {

		this.interval = interval;
		this.trackerId = trackerId;
		this.complete = complete;
		this.incomplete = incomplete;
		this.peers = peers;
	}

	public int getComplete() {

		return complete;
	}

	public int getIncomplete() {

		return incomplete;
	}

	public int getInterval() {

		return interval;
	}

	public List<PeerAddress> getPeers() {

		return peers;
	}

	public String getTrackerId() {

		return trackerId;
	}

	/**
	 * Creates a new {@link TorrentResponse} from the response data retrieved from the tracker over HTTP.<br/>
	 * 
	 * An {@link IOException} is throws when the response contains a <em>failure reason</em> are any
	 * other unexpected / missing data.
	 */
	protected static TorrentResponse createFromHttpResponse(String response)  throws IOException{

			BValue bValue = BDecoder.decode(response).iterator().next();

			if (bValue.getType() != Type.MAP) {
				throw new IOException("Unexpected tracker response: " + bValue);
			}
			Map<BValue, BValue> trackerResponseMap = bValue.getMap();
			if (trackerResponseMap.containsKey("failure reason")) {
				throw new IOException(trackerResponseMap
						.get("failure reason").toString());
			}
			Long interval =
					trackerResponseMap.get(new BValue("interval")).getInteger();
		if (interval == null) {
			throw new IOException(
					"<interval> is missing from the trackers response");
		}
			BValue trackerIdValue = trackerResponseMap.get(new BValue("trackerId"));
			//can be null
			String trackerId = trackerIdValue == null
					? null
					: trackerIdValue.getString();

			Long complete =
					trackerResponseMap.get(new BValue("complete")).getInteger();
		if (complete == null) {
			throw new IOException(
					"<complete> is missing from the trackers response");
		}

			Long incomplete =
					trackerResponseMap.get(new BValue("incomplete")).getInteger();
		if (incomplete == null) {
			throw new IOException(
					"<incomplete> is missing from the trackers response");
		}

			BValue peersValue = trackerResponseMap.get(new BValue("peers"));
		if (peersValue == null || peersValue.getType() != Type.STRING) {
			throw new IOException("<peers> is missing or invalid: "
					+ peersValue);
		}
			List<PeerAddress> peers = parsePeers(peersValue);
			
			

		return new TorrentResponse(
				interval.intValue(),
				trackerId,
				complete.intValue(),
				incomplete.intValue(),
				peers);

	}

	private static List<PeerAddress> parsePeers(BValue peersValue)
																	throws IOException {

		List<PeerAddress> peers = new ArrayList<PeerAddress>();
		byte[] peersData = peersValue.getByteString();
		if (peersData.length % 6 != 0) {
			throw new IOException("Invalid peer data: Not multple of 6 bytes:"
					+ peersData.length);
		}

		for (int i = 0; i < peersData.length; i += 6) {
			peers.add(PeerAddress.createFromByteString(peersData, i, 6));
		}

		return peers;
	}

}


